package partB;

import java.util.ArrayList;
import java.util.Arrays;

import binary_evolution.Binary_Genetic_Operators;
import misc.not_Legal_Exception;
import evolution_superclasses.Evolutionary_Loop;
import evolution_superclasses.Genotype;
import evolution_superclasses.Phenotype;

public class TaskB extends Evolutionary_Loop {
	private B_Population _bpop;
	private B_Fitness_Evaluation _bfe;
	private B_Genetic_Operators _bgo;
	
	private ArrayList<Double> _averageEntropy;
	private int _highestFrom = 0;
	private double _allTimeHighest = 0;
	private Genotype _allTimeHighestIndividual;
	private String _EntropyTitle = "Result", _EntropyFilename = "chart";

	public TaskB() {
		super();
		set_plotList(new boolean[]{true,true,false,true});
		_averageEntropy = new ArrayList<Double>();
	}

	public String get_EntropyTitle() {
		return _EntropyTitle;
	}

	public void set_EntropyTitle(String entropyTitle) {
		_EntropyTitle = entropyTitle;
	}

	public String get_EntropyFilename() {
		return _EntropyFilename;
	}

	public void set_EntropyFilename(String entropyFilename) {
		_EntropyFilename = entropyFilename;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		TaskB b = new TaskB();
		boolean die = false;
		try {
			b.begin();
		} catch (Exception e) {
			die = true;
			e.printStackTrace();
		}
//		if(!die){
//			try {
//				System.out.println("Again? (1/0)");
//				if(b._ih.readInt()==1){
//					main(null);
//					return;
//				}
//			} catch (Exception e) {
//				e.printStackTrace();
//			}
//		}
	}
	
	@Override
	public void begin() throws Exception{
		clearArrayLists();
		_averageEntropy.clear();

//		set_title("B = 20, R = 0, SR = 1");
//		set_filename("/B/signature2");
		set_title("title");
		set_filename("/B/chart");
		set_EntropyTitle("Entropy for "+get_title());
		set_EntropyFilename(get_filename()+"_entropy");
		_plot = false;
		
		initializePrint(true);
		initializeEndConditions(-1);
		initializePopulationSize(-1);
		initializeGenotypeSize(-1);
		initializeGeneticOperatiors(-1,-1);
		initializeValueMutationRate(-1);
		initializeEliteism(-1);
		initializeFitnessEvaluation(0);//It's the one below, that matters
		initializeFitnessValues(-1,-1);
		initializeAdultPoolSize(-1);
		initializeSelectionProtocol(-1);
		initializePopulation();
		initializeSelectionMechanism(-1);
		
		evolutionLoop();
		if(!_print)//Print them anyway
			printEndResults();
	}

	@Override
	protected void initializeGenotypeSize(int i) {
		if(i>-1){
			_genSize = i*4;
			return;
		}
		System.out.println("How many battles are there? (int)");
		try {
			_genSize = _ih.readInt()*4;
			if(_genSize<=0)throw new Exception();
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeGenotypeSize(i);
		}
	}

	@Override
	protected void initializeFitnessEvaluation(int i) throws not_Legal_Exception, Exception {
		_bfe = new B_Fitness_Evaluation(0);
	}
	
	private void initializeFitnessValues(double refactor, double moraleLoss) throws not_Legal_Exception, Exception {
		if(refactor>-1){
			_bfe.set_refactor(refactor);
			_bfe.set_moraleLoss(moraleLoss);
			return;
		}
		try {
			System.out.println("What is your redistribution factor? (double between 0 and 1)");
			double tmp = _ih.readDouble();
			System.out.println("What is your morale loss factor? (double between 0 and 1)");
			double tmp1 = _ih.readDouble();
			if(tmp < 0 || tmp > 1 || tmp1 < 0 || tmp1 > 1)throw new Exception();
			_bfe.set_refactor(tmp);
			_bfe.set_moraleLoss(tmp1);
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeFitnessValues(refactor,moraleLoss);
		}
	}

	protected void initializeValueMutationRate(double m) {
		if(m>-1){
			_bgo.set_valueMutationRate(m);
			return;
		}
		try {
			System.out.println("At what rate should genotype bit mutate? (type double, between 0 and 1)");
			double tmp = _ih.readDouble();
			if(tmp < 0 || tmp > 1)
				throw new IllegalArgumentException();
			_bgo.set_valueMutationRate(tmp);
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeValueMutationRate(m);
			return;
		}
	}

	@Override
	protected void initializeGeneticOperatiors(double m, double c) {
		if(m>-1){
			_mutationRate = m;
			_crossOverRate = c;
			_bgo = new B_Genetic_Operators(_genSize);
			return;
		}
		try {
			System.out.println("At what rate should genotypes mutate? (type double, between 0 and 1)");
			_mutationRate = _ih.readDouble();
			System.out.println("At what rate should genotypes suffer crossOvers? (type double, between 0 and 1)");
			_crossOverRate = _ih.readDouble();
			if(_mutationRate < 0 || _mutationRate > 1 || _crossOverRate < 0 || _crossOverRate > 1)
				throw new IllegalArgumentException();
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeGeneticOperatiors(m,c);
			return;
		}
		_bgo = new B_Genetic_Operators(_genSize);
	}

	@Override
	protected void initializePopulation() {
		_bpop = new B_Population(_genSize, _popSize);
	}

	@Override
	protected void initializeAndDevelopGeneration() throws not_Legal_Exception {
		int index = 0;
    	double variance = 0,standardDev, avg,total = 0, avgEntropy = 0;
		_generation = new B_Phenotype[_popSize];
		
		for(Genotype g : _bpop.get_genotypes()){
			g.generatePhenotype();
			_generation[index++] = g.get_phenotype();
		}
		
		_bfe.evaluateFitness(_generation);
		
		for(Phenotype p : _generation){
			total += p.get_fitness();
			avgEntropy += ((B_Phenotype)p).get_entropy();
		}
		
		avg = total/_popSize;


		for(Phenotype p : _generation){
    		variance += Math.pow(p.get_fitness()-avg,2);
    	}
    	standardDev = Math.sqrt(variance/_popSize);
		
		Arrays.sort(_generation);
		
		_averageEntropy.add(avgEntropy/_popSize);
		
		if(_printEach && (get_generationNumber()%(_origGenerationLimit/20))==0){
			System.out.println("");
			for(int i = 0; i <_generation.length; i++){
				System.out.println("Battle plan: "+_generation[i]+
						"Entropy:\t"+((B_Phenotype)_generation[i]).get_entropy()+
						"\tFitness value:\t"+_generation[i].get_fitness()+
						"\t Average: "+(_generation[i].get_fitness()/((_popSize-1)*(_genSize/4))));
			}
		
			System.out.println("\nGeneration "+get_generationNumber()+"\nBest fitness: "+_generation[0].get_fitness());
			System.out.println("Total fitness: "+total);
			System.out.println("Average entroypy: "+_averageEntropy.get(get_generationNumber()-1));
		}
		set_bestFitness(_generation[0].get_fitness());
		set_bestInduvidual(_generation[0].get_genotype());
		set_totalFitness(total);
		set_worstFitness(_generation[_popSize-1].get_fitness());
		
		_max.add(get_bestFitness());//((_popSize-1)*(_genSize/4)));
		_avg.add(avg);//((_popSize-1)*(_genSize/4)));
		_min.add(get_worstFitness());//((_popSize-1)*(_genSize/4)));
		_stdDerivance.add(standardDev);
	}

	@Override
	protected void createNewPopulation(){
		double best = get_bestFitness();
		boolean modified = false;
		
		if(best>_allTimeHighest){
			_allTimeHighest = best;
			_highestFrom = get_generationNumber()-1;
			B_Genotype bg1 = new B_Genotype(_genSize);
			bg1.set_binaryArray(((B_Genotype)_bestInduvidual).get_binaryArray().clone());
			_allTimeHighestIndividual = bg1;
		}
		
//		if(best==(_popSize-1)*(_genSize/4)*2){
//			finished();
//			return;
//		}
		
		int nr = 0, i = 0;
		Genotype[] tmp = new Genotype[_popSize];		

		while(i < _freepass){
			B_Genotype bg1 = new B_Genotype(_genSize);

			bg1.set_binaryArray(((B_Genotype)_bsp.get_adultPool()[i].get_genotype()).get_binaryArray().clone());
			tmp[i++] = bg1;
		}
		
		while(nr < _popSize-_freepass){
			B_Genotype bg1 = new B_Genotype(_genSize);
			B_Genotype bg2 = new B_Genotype(_genSize);

			bg1.set_binaryArray(((B_Genotype)_bsm.get_parents()[nr++].get_genotype()).get_binaryArray().clone());
			bg2.set_binaryArray(((B_Genotype)_bsm.get_parents()[((nr++)%_bsm.get_parents().length)].get_genotype()).get_binaryArray().clone());

			if( _crossOverRate > _rand.nextDouble()){
				_bgo.crossOver(bg1, bg2);
				modified = true;
			}

			if( _mutationRate > _rand.nextDouble()){
				_bgo.mutation(bg1);
				modified = true;
			}
			if( _mutationRate > _rand.nextDouble()){
				_bgo.mutation(bg2);
				modified = true;
			}
			
			//Weights should max be one
			if(modified){
				bg1.validate();
				bg2.validate();
			}
			
			tmp[i++] = bg1;
			if(i<_popSize) //In case it's an odd number.
			tmp[i++] = bg2;	
		}
		_bpop.set_genotypes(tmp);	
	}
	
	@Override
	protected void printEndResults() {
		if(_plot)_plotter.plotEntropy(get_EntropyTitle(), get_EntropyFilename(), _averageEntropy);
		System.out.println("\nEnd fitness: "+_endBestFitness+" after "+(get_generationNumber()-1)+" generations\t(Average : "+(_endBestFitness/((_popSize-1)*(_genSize/4)))+")");
		if(_print){
			System.out.println("The best individual at the end was this this:");
			System.out.println(_bestInduvidual+" Wich translate into"+((B_Phenotype)_bestInduvidual.get_phenotype()).toActualString());
			System.out.println("\nThe all time best individual was this from "+_highestFrom+", with fitness of "+_allTimeHighest+"\t(Average : "+(_allTimeHighest/((_popSize-1)*(_genSize/4)))+")");
			System.out.println(_allTimeHighestIndividual+" Wich translate into"+((B_Phenotype)_allTimeHighestIndividual.generatePhenotype()).toActualString());
		}
	}
}
